//--------------------------------------------------------------------------------------------------------------------
//Open Source Smart for Home (OSS4Home)
//
//The MIT License (MIT)
//
//Copyright (c) 2015 Sascha Deeg, Kim Janek Triebig, Michael Etter
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
//documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 
//rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
//permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all copies or substantial portions of
//the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
//BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
//NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//--------------------------------------------------------------------------------------------------------------------

package de.oss4home.cc.business;
import org.apache.logging.log4j.Level;
import org.nfunk.jep.*;

import com.sun.mail.imap.OlderTerm;

import de.oss4home.cc.data.DbManager;
import de.oss4home.cc.dto.DeviceConfigDTO;
import de.oss4home.cc.dto.DeviceTyp;
import de.oss4home.cc.dto.DeviceValueDTO;
import de.oss4home.exceptions.OSS4HomeException;
import de.oss4home.exceptions.OSS4HomeJpaException;
import de.oss4home.bc.dto.BcDeviceConfigDTO;
import de.oss4home.bc.dto.BcDeviceValueDTO;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Transient;

/**
 * Klasse zum Verwalten der Devices eines Controllers
 * 
 * @author Kim
 *
 */
@Entity
public class Device {

	
	//@Column(name = "deviceID")
	@Id
	@Column(name = "ID")
	private String id; // SDB

	private String name; // SDB

	@Transient
	private String value;
	@Transient
	private DeviceTyp typ;
	@Transient
	private boolean connected;
	@Transient
	private boolean  active;
	@Transient
	private boolean triggerAble;
	@Transient
	private boolean wasToggled = false;
	
	@Transient
	private String oldValue;

	@ManyToOne(fetch = FetchType.EAGER)
	private Controller controller; // SDB

	private String calculationFormular; // SDB

	private String unit; // SDB

	/*
	 * Das sollte durch die DeviceInfo.class wegfallen da das Device selbst
	 * keine relation zu den Gruppen hat
	 * 
	 * @ManyToMany
	 * 
	 * @JoinTable( name="Devices_Groups",
	 * joinColumns=@JoinColumn(name="Device_ID", referencedColumnName="ID"),
	 * inverseJoinColumns=@JoinColumn(name="Group_ID",
	 * referencedColumnName="ID")) private List<Group> groups;
	 */

	public Device(Controller controller) {
		this.controller = controller;
	}

	/**
	 * @return the id
	 */
	public String getId() {
		return id;
	}

	/**
	 * @param id
	 *            the id to set
	 */
	public void setId(String id) {
		this.id = id;
		if(this.getName() == null || this.getName().isEmpty()){
			this.setName(id);
		}
	}

	public Device() {

	}

	/**
	 * Updates the Device configuration related fields with the data from the
	 * DTO then TODO
	 * 
	 * @param config
	 */
	public void setConfigFromClient(DeviceConfigDTO config) throws OSS4HomeException {
		this.setName(config.getName());
		this.setUnit(config.getUnit());
		this.setCalculationFormular(config.getCalculationFormula());
		this.saveToDb();
		
		
		if(config.getType() != this.typ ||
				   config.getActive() != this.active)
		{
		BcDeviceConfigDTO data = new BcDeviceConfigDTO();
		data.setId(config.getId());
		data.setType(config.getType());
		data.setActive(config.getActive());
	
			controller.getControllerWsHandler().updateDeviceConfig(data);
			try
			{
			   OSS4HomeManager.getInstance().syncDeviceConfig();
			}catch(Exception ex)
			{
				OSS4HomeManager.getInstance().getLogger().log(Level.ERROR, ex);
			}
		}
	}

	/**
	 * Updates the Device configuration related fields with the data from the
	 * BcDeviceConfigDTO
	 * 
	 * @param config
	 */
	public void setConfigFromController(BcDeviceConfigDTO config) {
		if (!config.getId().equals(id))
			return;
		this.setTyp(config.getType());
		this.setConnected(config.getConnected());
		// this.setTriggerAble(config.getActivitySensing());
		this.setActive(config.getActive());

	}

	/**
	 * Updates the Devices Value with the Value from the given BcDeviceValueDTO
	 * 
	 * @param value
	 */
	public void setValueFromController(BcDeviceValueDTO value,boolean system) {
		if (value.getValue() == null)
			return;
		if(this.oldValue == null)
		{
			oldValue =  value.getValue();
		}
		if (system) {
			if (this.oldValue.equals(value.getValue())) {
				this.setValue(value.getValue());
				oldValue =  value.getValue();
				this.setWasToggled(false);
			} else {
				this.setValue(value.getValue());
				oldValue =  value.getValue();
				this.setWasToggled(true);
			}
		}
		else
		{
			this.value = value.getValue();
		}
		
		

	}
	
	
	public void setValueFromClient(DeviceValueDTO dto) throws OSS4HomeException {
		if (!dto.getId().equals(id))
			return;
		BcDeviceValueDTO bcDTO = new BcDeviceValueDTO();
		bcDTO.setId(dto.getId());
		bcDTO.setValue(dto.getValue());
		controller.getControllerWsHandler().setDeviceValue(bcDTO);

	}
	 

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getValue() {
		return value;
	}

	private void setValue(String value) {
		if(this.getTyp() == DeviceTyp.SENSOR
				&& this.calculationFormular != null
				&& (!this.calculationFormular.isEmpty()))
		{
			try
			{
			JEP jep = new JEP();
			jep.addVariable("x", Double.parseDouble(value));
			jep.parseExpression(this.calculationFormular);
			this.value = (new Double(jep.getValue())).toString();
			}catch(Exception ex)
			{
				this.value = value;
			}
		}
		else
		{
			this.value = value;
		}
		
	}

	public DeviceTyp getTyp() {
		return typ;
	}

	private void setTyp(DeviceTyp typ) {
		this.typ = typ;
	}

	public boolean isConnected() {
		return connected;
	}

	public void setConnected(boolean connected) {
		this.connected = connected;
	}

	public boolean isActive() {
		return  active;
	}

	public void setActive(boolean active) {
		this. active =  active;
	}

	public boolean isTriggerAble() {
		return triggerAble;
	}

	public void setTriggerAble(boolean triggerAble) {
		this.triggerAble = triggerAble;
	}

	public Controller getController() {
		return controller;
	}

	public void setController(Controller controller) {
		this.controller = controller;
	}

	public String getCalculationFormular() {
		return calculationFormular;
	}

	public void setCalculationFormular(String calculationFormular) {
		this.calculationFormular = calculationFormular;
	}

	public String getUnit() {
		return unit;
	}

	public void setUnit(String unit) {
		this.unit = unit;
	}

	/*
	 * public List<Group> getGroups() { return groups; }
	 * 
	 * public void setGroups(List<Group> groups) { this.groups = groups; }
	 */

	/**
	 * Used by DB manager to update the Device from the DB
	 * 
	 * @param updatedDevice
	 */
	public void updateDbRelevantData(Device updatedDevice) {
		if (this.id == updatedDevice.id) {
			this.name = updatedDevice.name;
			this.controller = updatedDevice.controller;
			this.calculationFormular = updatedDevice.calculationFormular;
			this.unit = updatedDevice.unit;
		}

	}

	/**
	 * Saves the Device in the DB.
	 * @throws OSS4HomeJpaException 
	 */
	public void saveToDb() throws OSS4HomeJpaException {
		DbManager.getInstance().updateDevice(this);
	}

	/**
	 * Creates a DeviceInfo Object with data from this Device to use with a
	 * Group
	 * 
	 * @return
	 */
	public DeviceInfo getDeviceInfoForGroup() {
		DeviceInfo deviceInfo = new DeviceInfo();
		deviceInfo.setControllerId(this.getController().getId());
		deviceInfo.setDeviceId(this.getId());
		return deviceInfo;
	}

	/**
	 * If the Device is Active, apply the Values from the DTO to it.
	 * 
	 * @param dto
	 */
	/*public void applyBcDeviceConfigDTO(BcDeviceConfigDTO dto) {
		if (dto == null || !this.active) {
			return;
		}
		this.setTyp(dto.getType());
		this.setActive(dto.getActive());
		this.setConnected(dto.getConnected());
	}*/

	/**
	 * If the Device is Active, apply the Values from the DTO to it
	 * 
	 * @param dto
	 */
	/*public void applyBcDeviceValueDTO(BcDeviceValueDTO dto) {
		if (dto == null || !this.active) {
			return;
		}
		this.setValue(dto.getValue());
	}*/

	public DeviceConfigDTO getAsDeviceConfigDTO() {
		DeviceConfigDTO dto = new DeviceConfigDTO();
		dto.setActive(this.isActive());
		dto.setCalculationFormula(this.getCalculationFormular());
		dto.setUnit(this.getUnit());
		dto.setId(this.getId());
		dto.setType(this.getTyp());
		dto.setName(this.getName());
		dto.setControllerId(new Integer(controller.getId()).toString());
		return dto;
	}
	
	//public void setFromDeviceConfigDTO(DeviceConfigDTO dto){
	//	this.setActive(dto.getActive());
	//	this.setCalculationFormular(dto.getCalculationFormula());
	//	this.setUnit(dto.getEinheit());
	//	this.setTyp(dto.getType());
	//}

	public DeviceValueDTO getAsDeviceValueDTO() {
		DeviceValueDTO dto = new DeviceValueDTO();
		dto.setId(this.getId());
		dto.setName(this.getName());
		dto.setValue(this.getValue());
		dto.setUnit(this.getUnit());
		dto.setControllerId(new Integer(this.controller.getId()).toString());
		return dto;

	}
	
	public void setFromDeviceValueDTO(DeviceValueDTO dto){
		this.setName(dto.getName());
		this.setValue(dto.getValue());
	}

	public boolean isWasToggled() {
		return wasToggled;
	}

	public void setWasToggled(boolean wasToggled) {
		this.wasToggled = wasToggled;
	}

}
